<template>
  <div>
    <!-- 控制区域 -->
    <div class="control-panel">
      <el-select v-model="selectedDataMode" placeholder="选择数据模式" @change="handleDataChange" class="data-select">
        <el-option label="仓库1" value="original"></el-option>
        <el-option label="仓库2" value="sparse"></el-option>
        <el-option label="仓库3" value="dense"></el-option>
        <el-option label="仓库4" value="warning"></el-option>
        <el-option label="仓库5" value="danger"></el-option>
      </el-select>
    </div>

    <div ref="container" class="three-container"></div>

    <!-- 点击弹窗 -->
    <div id="popup" class="hidden popup" ref="popup">
      <div id="popupContent" class="popup-content"></div>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

export default {
  name: 'BoxStack',
  data () {
    return {
      // 场景核心对象
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      animationFrameId: null,

      // 可交互对象集合
      boxes: [],
      trays: [],

      // 选中状态管理
      selectedBox: null,
      selectedTray: null,

      // 配置参数
      trayConfig: {
        rows: 2,
        cols: 4,
        spacing: 3
      },
      boxConfig: {
        rows: 4,
        cols: 4,
        layers: 4,
        size: 1,
        gap: 0.3,
        layerGap: 0.4
      },
      statusColors: {
        '正常': 'hsl(120, 80%, 75%)',
        '警告': 'hsl(60, 80%, 75%)',
        '危险': 'hsl(0, 80%, 75%)',
        '空置': 'hsl(200, 80%, 75%)',
        '满仓': 'hsl(30, 80%, 75%)',
        '默认': '#ffdf5d'
      },

      // 数据模式
      selectedDataMode: 'original', // 默认原始数据
      originalBoxesData: [], // 存储原始数据
      boxesData: [], // 当前使用的数据

      // 随机数据生成用的基础数据
      randomDataSources: {
        categories: ["智能手机", "平板电脑", "笔记本电脑", "T恤", "牛仔裤", "外套", "饼干", "巧克力", "方便面", "感冒药", "退烧药", "创可贴", "积木", "游戏手柄", "小说", "充电器", "耳机", "鼠标"],
        statuses: ["正常", "警告", "危险", "满仓"]
      }
    };
  },
  mounted () {
    // 初始化原始数据
    this.originalBoxesData = this.generateOriginalData();
    this.boxesData = [...this.originalBoxesData];

    this.initThree();
    this.createTrays();
    this.animate();
    window.addEventListener('resize', this.onWindowResize.bind(this));
  },
  beforeDestroy () {
    this.cleanup();
    window.removeEventListener('resize', this.onWindowResize.bind(this));
  },
  methods: {
    // 生成原始数据
    generateOriginalData () {
      return [
        // 第1行第1列托盘
        {
          trayId: 'tray-1-1',
          boxes: [
            { id: 'box-1-1-1-1-l1', row: 1, col: 1, layer: 1, name: "智能手机", quantity: 25, status: "正常" },
            { id: 'box-1-1-1-2-l1', row: 1, col: 2, layer: 1, name: "T恤", quantity: 40, status: "正常" },
            { id: 'box-1-1-1-3-l1', row: 1, col: 3, layer: 1, name: "饼干", quantity: 30, status: "警告" },
            { id: 'box-1-1-1-4-l1', row: 1, col: 4, layer: 1, name: "感冒药", quantity: 15, status: "危险" },
            { id: 'box-1-1-2-1-l1', row: 2, col: 1, layer: 1, name: "积木", quantity: 50, status: "正常" },
            { id: 'box-1-1-2-2-l1', row: 2, col: 2, layer: 1, name: "小说", quantity: 20, status: "正常" },
            { id: 'box-1-1-1-1-l2', row: 1, col: 1, layer: 2, name: "平板电脑", quantity: 20, status: "正常" },
            { id: 'box-1-1-1-2-l2', row: 1, col: 2, layer: 2, name: "牛仔裤", quantity: 35, status: "正常" },
            { id: 'box-1-1-3-3-l2', row: 3, col: 3, layer: 2, name: "巧克力", quantity: 10, status: "警告" },
            { id: 'box-1-1-4-1-l3', row: 4, col: 1, layer: 3, name: "充电器", quantity: 60, status: "满仓" },
            { id: 'box-1-1-4-2-l3', row: 4, col: 2, layer: 3, name: "运动鞋", quantity: 18, status: "正常" }
          ]
        },
        // 第1行第2列托盘
        {
          trayId: 'tray-1-2',
          boxes: [
            { id: 'box-1-2-1-1-l1', row: 1, col: 1, layer: 1, name: "笔记本电脑", quantity: 15, status: "正常" },
            { id: 'box-1-2-1-2-l1', row: 1, col: 2, layer: 1, name: "外套", quantity: 30, status: "正常" },
            { id: 'box-1-2-2-3-l1', row: 2, col: 3, layer: 1, name: "方便面", quantity: 45, status: "满仓" },
            { id: 'box-1-2-3-1-l2', row: 3, col: 1, layer: 2, name: "耳机", quantity: 22, status: "正常" },
            { id: 'box-1-2-3-2-l2', row: 3, col: 2, layer: 2, name: "帽子", quantity: 33, status: "正常" },
            { id: 'box-1-2-3-3-l2', row: 3, col: 3, layer: 2, name: "罐头", quantity: 8, status: "警告" },
            { id: 'box-1-2-4-4-l4', row: 4, col: 4, layer: 4, name: "退烧药", quantity: 5, status: "危险" }
          ]
        },
        // 第1行第3列托盘
        {
          trayId: 'tray-1-3',
          boxes: [
            { id: 'box-1-3-1-1-l1', row: 1, col: 1, layer: 1, name: "智能手表", quantity: 12, status: "正常" },
            { id: 'box-1-3-1-2-l1', row: 1, col: 2, layer: 1, name: "袜子", quantity: 100, status: "满仓" },
            { id: 'box-1-3-2-3-l2', row: 2, col: 3, layer: 2, name: "面包", quantity: 18, status: "警告" },
            { id: 'box-1-3-2-4-l2', row: 2, col: 4, layer: 2, name: "创可贴", quantity: 70, status: "正常" },
            { id: 'box-1-3-3-1-l3', row: 3, col: 1, layer: 3, name: "游戏手柄", quantity: 9, status: "正常" },
            { id: 'box-1-3-3-2-l3', row: 3, col: 2, layer: 3, name: "围巾", quantity: 26, status: "正常" },
            { id: 'box-1-3-4-3-l4', row: 4, col: 3, layer: 4, name: "薯片", quantity: 32, status: "正常" }
          ]
        },
        // 第1行第4列托盘
        {
          trayId: 'tray-1-4',
          boxes: [
            { id: 'box-1-4-1-1-l1', row: 1, col: 1, layer: 1, name: "摄像头", quantity: 17, status: "正常" },
            { id: 'box-1-4-1-4-l1', row: 1, col: 4, layer: 1, name: "止痛药", quantity: 20, status: "正常" },
            { id: 'box-1-4-2-2-l2', row: 2, col: 2, layer: 2, name: "手套", quantity: 42, status: "正常" },
            { id: 'box-1-4-2-3-l2', row: 2, col: 3, layer: 2, name: "糖果", quantity: 55, status: "满仓" },
            { id: 'box-1-4-3-1-l3', row: 3, col: 1, layer: 3, name: "移动硬盘", quantity: 14, status: "正常" },
            { id: 'box-1-4-4-2-l4', row: 4, col: 2, layer: 4, name: "毛衣", quantity: 19, status: "正常" },
            { id: 'box-1-4-4-4-l4', row: 4, col: 4, layer: 4, name: "消炎药", quantity: 7, status: "危险" }
          ]
        },
        // 第2行第1列托盘
        {
          trayId: 'tray-2-1',
          boxes: [
            { id: 'box-2-1-1-3-l1', row: 1, col: 3, layer: 1, name: "火腿肠", quantity: 28, status: "正常" },
            { id: 'box-2-1-1-4-l1', row: 1, col: 4, layer: 1, name: "纱布", quantity: 36, status: "正常" },
            { id: 'box-2-1-2-1-l2', row: 2, col: 1, layer: 2, name: "U盘", quantity: 44, status: "正常" },
            { id: 'box-2-1-2-2-l2', row: 2, col: 2, layer: 2, name: "裙子", quantity: 11, status: "正常" },
            { id: 'box-2-1-3-3-l3', row: 3, col: 3, layer: 3, name: "果冻", quantity: 62, status: "满仓" },
            { id: 'box-2-1-3-4-l3', row: 3, col: 4, layer: 3, name: "酒精", quantity: 9, status: "警告" },
            { id: 'box-2-1-4-1-l4', row: 4, col: 1, layer: 4, name: "鼠标", quantity: 37, status: "正常" }
          ]
        },
        // 第2行第2列托盘
        {
          trayId: 'tray-2-2',
          boxes: [
            { id: 'box-2-2-1-1-l1', row: 1, col: 1, layer: 1, name: "键盘", quantity: 23, status: "正常" },
            { id: 'box-2-2-1-2-l1', row: 1, col: 2, layer: 1, name: "皮鞋", quantity: 16, status: "正常" },
            { id: 'box-2-2-2-3-l2', row: 2, col: 3, layer: 2, name: "蛋糕", quantity: 5, status: "危险" },
            { id: 'box-2-2-2-4-l2', row: 2, col: 4, layer: 2, name: "碘伏", quantity: 29, status: "正常" },
            { id: 'box-2-2-3-1-l3', row: 3, col: 1, layer: 3, name: "路由器", quantity: 8, status: "正常" },
            { id: 'box-2-2-3-2-l3', row: 3, col: 2, layer: 3, name: "背包", quantity: 21, status: "正常" },
            { id: 'box-2-2-4-3-l4', row: 4, col: 3, layer: 4, name: "坚果", quantity: 48, status: "满仓" }
          ]
        },
        // 第2行第3列托盘
        {
          trayId: 'tray-2-3',
          boxes: [
            { id: 'box-2-3-1-1-l1', row: 1, col: 1, layer: 1, name: "打印机", quantity: 7, status: "正常" },
            { id: 'box-2-3-1-4-l1', row: 1, col: 4, layer: 1, name: "血压计", quantity: 10, status: "正常" },
            { id: 'box-2-3-2-2-l2', row: 2, col: 2, layer: 2, name: "衬衫", quantity: 31, status: "正常" },
            { id: 'box-2-3-2-3-l2', row: 2, col: 3, layer: 2, name: "奶粉", quantity: 13, status: "警告" },
            { id: 'box-2-3-3-1-l3', row: 3, col: 1, layer: 3, name: "扫描仪", quantity: 4, status: "警告" },
            { id: 'box-2-3-4-2-l4', row: 4, col: 2, layer: 4, name: "羽绒服", quantity: 19, status: "正常" },
            { id: 'box-2-3-4-3-l4', row: 4, col: 3, layer: 4, name: "麦片", quantity: 39, status: "正常" }
          ]
        },
        // 第2行第4列托盘
        {
          trayId: 'tray-2-4',
          boxes: [
            { id: 'box-2-4-1-2-l1', row: 1, col: 2, layer: 1, name: "运动鞋", quantity: 27, status: "正常" },
            { id: 'box-2-4-1-3-l1', row: 1, col: 3, layer: 1, name: "咖啡", quantity: 51, status: "满仓" },
            { id: 'box-2-4-2-1-l2', row: 2, col: 1, layer: 2, name: "显示器", quantity: 6, status: "正常" },
            { id: 'box-2-4-2-4-l2', row: 2, col: 4, layer: 2, name: "体温计", quantity: 24, status: "正常" },
            { id: 'box-2-4-3-2-l3', row: 3, col: 2, layer: 3, name: "短裤", quantity: 34, status: "正常" },
            { id: 'box-2-4-3-3-l3', row: 3, col: 3, layer: 3, name: "茶叶", quantity: 18, status: "正常" },
            { id: 'box-2-4-4-1-l4', row: 4, col: 1, layer: 4, name: "投影仪", quantity: 3, status: "危险" },
            { id: 'box-2-4-4-4-l4', row: 4, col: 4, layer: 4, name: "口罩", quantity: 80, status: "满仓" }
          ]
        }
      ];
    },

    // 根据模式生成随机数据
    generateRandomData (mode) {
      const randomData = [];
      const { rows: trayRows, cols: trayCols } = this.trayConfig;
      const { rows: boxRows, cols: boxCols, layers: boxLayers } = this.boxConfig;
      const { categories, statuses } = this.randomDataSources;

      // 根据模式设置参数
      let boxCountRange, statusProbabilities;

      switch (mode) {
        case 'sparse': // 稀疏分布
          boxCountRange = [3, 8]; // 每个托盘3-8个盒子
          statusProbabilities = [0.6, 0.2, 0.1, 0.1]; // 正常占比高
          break;
        case 'dense': // 密集分布
          boxCountRange = [20, 30]; // 每个托盘20-30个盒子
          statusProbabilities = [0.6, 0.2, 0.1, 0.1];
          break;
        case 'warning': // 高警告比例
          boxCountRange = [10, 20];
          statusProbabilities = [0.3, 0.5, 0.1, 0.1]; // 警告占比高
          break;
        case 'danger': // 高危险比例
          boxCountRange = [10, 20];
          statusProbabilities = [0.3, 0.2, 0.4, 0.1]; // 危险占比高
          break;
        default:
          boxCountRange = [10, 20];
          statusProbabilities = [0.6, 0.2, 0.1, 0.1];
      }

      // 为每个托盘生成随机数据
      for (let trayRow = 1; trayRow <= trayRows; trayRow++) {
        for (let trayCol = 1; trayCol <= trayCols; trayCol++) {
          const trayId = `tray-${trayRow}-${trayCol}`;
          const boxes = [];

          // 根据模式生成不同数量的盒子
          const boxCount = Math.floor(Math.random() * (boxCountRange[1] - boxCountRange[0] + 1)) + boxCountRange[0];

          for (let i = 0; i < boxCount; i++) {
            // 随机生成行列层
            const row = Math.floor(Math.random() * boxRows) + 1;
            const col = Math.floor(Math.random() * boxCols) + 1;
            const layer = Math.floor(Math.random() * boxLayers) + 1;

            // 避免重复的位置
            const isDuplicate = boxes.some(box =>
              box.row === row && box.col === col && box.layer === layer
            );
            if (isDuplicate) continue;

            // 根据概率分布选择状态
            let statusIndex = 0;
            let random = Math.random();
            while (random > 0) {
              random -= statusProbabilities[statusIndex];
              if (random <= 0) break;
              statusIndex++;
            }
            const status = statuses[statusIndex];

            // 随机选择类别和数量
            const category = categories[Math.floor(Math.random() * categories.length)];
            const quantity = status === "满仓"
              ? Math.floor(Math.random() * 50) + 50  // 满仓状态数量更多
              : Math.floor(Math.random() * 40) + 5;  // 其他状态随机数量

            boxes.push({
              id: `box-${trayId}-${layer}-${row}-${col}`,
              row,
              col,
              layer,
              name: category,
              quantity,
              status
            });
          }

          randomData.push({ trayId, boxes });
        }
      }

      return randomData;
    },

    // 处理数据模式变化
    handleDataChange (mode) {
      // 清除现有盒子和托盘
      this.boxes.forEach(box => this.scene.remove(box));
      this.trays.forEach(tray => this.scene.remove(tray));
      this.boxes = [];
      this.trays = [];

      // 重置选中状态
      this.resetSelection();
      this.$refs.popup.classList.add('hidden');

      // 更新数据并重新创建
      this.boxesData = mode === 'original'
        ? [...this.originalBoxesData]
        : this.generateRandomData(mode);

      this.createTrays();
    },

    initThree () {
      // 场景初始化
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0xf0f2f5);

      // 相机设置
      const container = this.$refs.container;
      const aspect = container.clientWidth / container.clientHeight;
      this.camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
      this.camera.position.set(15, 20, 25);
      this.camera.lookAt(0, 0, 0);

      // 渲染器设置
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.shadowMap.enabled = true;
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      container.appendChild(this.renderer.domElement);

      // 控制器设置
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.dampingFactor = 0.05;
      this.controls.minDistance = 5;
      this.controls.maxDistance = 50;
      this.controls.maxPolarAngle = Math.PI / 2;

      // 增强环境光（提升整体亮度）
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
      this.scene.add(ambientLight);

      // 增强方向光（提升直射光亮度）
      const mainLight = new THREE.DirectionalLight(0xffffff, 1.0);
      mainLight.position.set(10, 20, 15);
      mainLight.castShadow = true;
      mainLight.shadow.mapSize.set(2048, 2048);
      mainLight.shadow.camera.near = 0.5;
      mainLight.shadow.camera.far = 50;
      mainLight.shadow.camera.left = -25;
      mainLight.shadow.camera.right = 25;
      mainLight.shadow.camera.top = 25;
      mainLight.shadow.camera.bottom = -25;
      mainLight.shadow.bias = -0.001;
      this.scene.add(mainLight);

      // 地板创建
      this.createFloor();
    },

    createFloor () {
      const floorSize = 50;
      const floorGeometry = new THREE.PlaneGeometry(floorSize, floorSize);
      const floorMaterial = new THREE.MeshStandardMaterial({
        color: '#fff',
        roughness: 0.5,
        metalness: 0.1
      });
      const floor = new THREE.Mesh(floorGeometry, floorMaterial);
      floor.rotation.x = -Math.PI / 2;
      floor.position.y = -0.3;
      floor.receiveShadow = true;
      this.scene.add(floor);

      // 网格辅助线
      const gridHelper = new THREE.GridHelper(
        floorSize,
        25,
        0xf0f0f0,  // 主网格线：极浅灰
        0xf8f8f8   // 辅助网格线：更浅的灰色
      );
      gridHelper.position.y = -0.29;
      this.scene.add(gridHelper);
    },

    createTrays () {
      const traySize = { width: 6, height: 0.5, depth: 6 };
      const totalWidth = this.trayConfig.cols * (traySize.width + this.trayConfig.spacing) - this.trayConfig.spacing;
      const totalDepth = this.trayConfig.rows * (traySize.depth + this.trayConfig.spacing) - this.trayConfig.spacing;
      const startX = -totalWidth / 2 + traySize.width / 2;
      const startZ = -totalDepth / 2 + traySize.depth / 2;

      for (let row = 0; row < this.trayConfig.rows; row++) {
        for (let col = 0; col < this.trayConfig.cols; col++) {
          const trayId = `tray-${row + 1}-${col + 1}`;
          const trayPosX = startX + col * (traySize.width + this.trayConfig.spacing);
          const trayPosZ = startZ + row * (traySize.depth + this.trayConfig.spacing);

          const tray = this.createTray(trayId, traySize, trayPosX, trayPosZ);
          this.scene.add(tray);
          this.trays.push(tray);

          this.createBoxesForTray(trayId, trayPosX, trayPosZ);
        }
      }

      this.setupEventListeners();
    },

    createTray (trayId, size, posX, posZ) {
      const geometry = new THREE.BoxGeometry(size.width, size.height, size.depth);
      const material = new THREE.MeshStandardMaterial({
        color: 0x8B4513,
        roughness: 0.7,
        metalness: 0.2
      });
      const tray = new THREE.Mesh(geometry, material);
      tray.position.set(posX, 0, posZ);
      tray.receiveShadow = true;

      tray.userData = {
        type: 'tray',
        id: trayId,
        status: '正常',
        originalColor: material.color.getHex()
      };

      return tray;
    },

    createBoxesForTray (trayId, trayPosX, trayPosZ) {
      const boxAreaWidth = this.boxConfig.cols * this.boxConfig.size + (this.boxConfig.cols - 1) * this.boxConfig.gap;
      const boxStartX = -boxAreaWidth / 2 + this.boxConfig.size / 2;
      const boxStartZ = -boxAreaWidth / 2 + this.boxConfig.size / 2;

      const trayData = this.boxesData.find(t => t.trayId === trayId);
      const trayBoxesData = trayData ? trayData.boxes : [];

      for (let layer = 1; layer <= this.boxConfig.layers; layer++) {
        for (let row = 1; row <= this.boxConfig.rows; row++) {
          for (let col = 1; col <= this.boxConfig.cols; col++) {
            const boxData = trayBoxesData.find(
              b => b.row === row && b.col === col && b.layer === layer
            );

            const box = this.createBox(
              row, col, layer,
              boxStartX, boxStartZ,
              trayPosX, trayPosZ,
              trayId,
              boxData
            );

            this.scene.add(box);
            this.boxes.push(box);
          }
        }
      }
    },

    createBox (row, col, layer, boxStartX, boxStartZ, trayPosX, trayPosZ, trayId, boxData) {
      const geometry = new THREE.BoxGeometry(
        this.boxConfig.size,
        this.boxConfig.size,
        this.boxConfig.size
      );

      const statusColor = boxData && boxData.status && this.statusColors[boxData.status];
      const color = statusColor || this.statusColors['默认'];
      const material = new THREE.MeshStandardMaterial({
        color: new THREE.Color(color),
        roughness: 0.3,
        metalness: 0.1,
        side: THREE.DoubleSide // 双面渲染，确保所有面可点击
      });

      const box = new THREE.Mesh(geometry, material);

      // 盒子Y坐标从托盘上方开始计算（托盘Y=0，第一层盒子底部Y=0.5）
      const yPos = this.boxConfig.size / 2 +
        (layer - 1) * (this.boxConfig.size + this.boxConfig.layerGap);
      box.position.set(
        trayPosX + boxStartX + (col - 1) * (this.boxConfig.size + this.boxConfig.gap),
        yPos,
        trayPosZ + boxStartZ + (row - 1) * (this.boxConfig.size + this.boxConfig.gap)
      );

      box.castShadow = true;
      box.receiveShadow = true;

      box.userData = {
        ...boxData,
        id: boxData?.id || `box-${trayId}-${layer}-${row}-${col}`,
        row,
        col,
        layer,
        trayId,
        type: 'box',
        originalColor: new THREE.Color(color).getHex()
      };

      return box;
    },

    setupEventListeners () {
      this.renderer.domElement.addEventListener('click', this.onMouseClick.bind(this));
    },

    onMouseClick (event) {
      if (!this.scene || !this.camera) return;

      const raycaster = new THREE.Raycaster();
      const mouse = new THREE.Vector2();
      const popup = this.$refs.popup;

      const rect = this.renderer.domElement.getBoundingClientRect();
      mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

      raycaster.setFromCamera(mouse, this.camera);
      const allClickable = [...this.boxes, ...this.trays];
      const intersects = raycaster.intersectObjects(allClickable, true);

      this.resetSelection();

      if (intersects.length > 0) {
        const clickedObject = this.getRootObject(intersects[0].object);

        if (clickedObject.userData.type === 'box') {
          this.handleBoxClick(clickedObject);
        } else if (clickedObject.userData.type === 'tray') {
          this.handleTrayClick(clickedObject);
        }
      } else {
        popup.classList.add('hidden');
      }

      event.stopPropagation();
    },

    getRootObject (object) {
      while (object.parent && object.parent.userData.type) {
        object = object.parent;
      }
      return object;
    },

    handleBoxClick (box) {
      box.material.color.setHex(0x3182ce);
      this.selectedBox = box;
      this.showPopup(box.userData);
    },

    handleTrayClick (tray) {
      tray.material.color.setHex(0x3182ce);
      this.selectedTray = tray;
      this.showPopup(tray.userData);
    },

    resetSelection () {
      if (this.selectedBox) {
        this.selectedBox.material.color.setHex(this.selectedBox.userData.originalColor);
        this.selectedBox = null;
      }

      if (this.selectedTray) {
        this.selectedTray.material.color.setHex(this.selectedTray.userData.originalColor);
        this.selectedTray = null;
      }
    },

    showPopup (data) {
      const popup = this.$refs.popup;
      const popupContent = popup.querySelector('#popupContent');

      if (!popup || !popupContent) return;

      if (data.type === 'box') {
        popupContent.innerHTML = `
          <h3 class="popup-title">盒子信息</h3>
          <p><span class="popup-label">名称:</span> ${data.name || '无'}</p>
          <p><span class="popup-label">数量:</span> ${data.quantity || 0}</p>
          <p><span class="popup-label">状态:</span> ${data.status || '未知'}</p>
          <p><span class="popup-label">层级:</span> ${data.layer || 0}</p>
          <p><span class="popup-label">托盘:</span> ${data.trayId || ''}</p>
        `;
      } else if (data.type === 'tray') {
        popupContent.innerHTML = `
          <h3 class="popup-title">托盘信息</h3>
          <p><span class="popup-label">ID:</span> ${data.id || '无'}</p>
          <p><span class="popup-label">状态:</span> ${data.status || '正常'}</p>
        `;
      }

      this.updatePopupPosition(data.type === 'box' ? this.selectedBox : this.selectedTray);
      popup.classList.remove('hidden');
    },

    updatePopupPosition (object) {
      if (!object || !this.renderer || !this.camera) return;

      const popup = this.$refs.popup;
      const pos = object.position.clone();
      pos.y += 1.2;

      const vector = pos.project(this.camera);
      const widthHalf = 0.5 * this.renderer.domElement.clientWidth;
      const heightHalf = 0.5 * this.renderer.domElement.clientHeight;

      const x = (vector.x * widthHalf) + widthHalf;
      const y = -(vector.y * heightHalf) + heightHalf;

      popup.style.left = `${x}px`;
      popup.style.top = `${y}px`;
      popup.style.transform = 'translate(-50%, -100%)';
    },

    onWindowResize () {
      const container = this.$refs.container;
      const width = container.clientWidth;
      const height = container.clientHeight;

      this.camera.aspect = width / height;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(width, height);
    },

    animate () {
      this.animationFrameId = requestAnimationFrame(() => this.animate());
      this.controls.update();
      this.renderer.render(this.scene, this.camera);
    },

    cleanup () {
      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId);
        this.animationFrameId = null;
      }

      if (this.renderer && this.$refs.container) {
        this.$refs.container.removeChild(this.renderer.domElement);
      }

      if (this.controls) {
        this.controls.dispose();
      }

      if (this.scene) {
        while (this.scene.children.length > 0) {
          const obj = this.scene.children[0];
          if (obj.geometry) obj.geometry.dispose();
          if (obj.material) {
            if (Array.isArray(obj.material)) {
              obj.material.forEach(m => m.dispose());
            } else {
              obj.material.dispose();
            }
          }
          this.scene.remove(obj);
        }
      }
    }
  }
};
</script>

<style scoped>
/* 控制区域样式 */
.control-panel {
  position: fixed;
  top: 70px;
  left: 100px;
  z-index: 999;
  padding: 10px;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.data-select {
  width: 200px;
}

.three-container {
  position: fixed;
  top: 50px;
  left: 0;
  width: 100%;
  height: 90vh;
  overflow: hidden;
}

/* 弹窗样式 */
.popup {
  position: absolute;
  z-index: 1000;
  background: white;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  padding: 16px;
  min-width: 240px;
  pointer-events: auto;
}

.popup.hidden {
  display: none;
}

.popup-title {
  margin: 0 0 12px 0;
  font-size: 16px;
  font-weight: 600;
  color: #1f2329;
  border-bottom: 1px solid #f0f0f0;
  padding-bottom: 8px;
}

.popup-content p {
  margin: 6px 0;
  font-size: 14px;
  line-height: 1.5;
}

.popup-label {
  color: #6b7280;
  font-weight: 500;
  display: inline-block;
  width: 60px;
}

::v-deep .el-dialog {
  display: none !important;
}
</style>